﻿# coding: utf-8
import wx
import xml.dom.minidom as minixml
import os
import shutil
import copy

from SkeletonExport import CSkeletonExport
from MeshExport import CMeshExport
from MeshExport import DlgMeshSelection
from MaterialExport import CMaterialExport

class CIGameConverter:
		def __init__(self):
				self._mtlExport = None
				self._meshExportList = []
				self._skeletonExportList = []
				self._texFilePathList = []
				self._hasSkin = False
				
		def __ExportMaterial(self, IGameXMLDoc, modelName):    
				""" Export all materials """
										
				# export the material
				self._mtlExport = CMaterialExport()
				self._mtlExport.DoExport(IGameXMLDoc, modelName)
								
		def __ExportMesh(self, IGameXMLDoc, modelName):
				""" Export all meshes """
				
				# selection the mesh which need to be exported.
				dlg = DlgMeshSelection(wx.GetApp()._mainFrame, IGameXMLDoc)
				if dlg.ShowModal() != wx.ID_OK:
						return
				
				meshExportList = dlg.GetMeshExportList()

				# export meshes.
				for meshName in meshExportList:
						meshExport = CMeshExport()
						self._meshExportList.append(meshExport)
						pathName = '%s/%s' % (wx.GetApp()._mainFrame._dbbMeshDir.GetValue(), modelName)
						pathName = os.path.normpath(pathName)
						meshExport.DoExport(IGameXMLDoc, meshName, pathName)
						if meshExport.HasSkin():
								self._hasSkin = True
				
		def __GetModelName(self, modelFile):
				""" Get the model name """
				modelName = modelFile[len(GLOBAL_SETTING._modelDir):]
				return modelName[:modelName.find('.')]        
		
		def __GetAnimationName(self, animationFileName):
				""" Get the animation name """
				
				dirName, fileBase = os.path.split(animationFileName)
				return fileBase[:fileBase.find('.')]
								
		def __ParseIGameXML(self, IGameXMLFile):
				""" Parse IGame XML file """
				
				print 'Parse IGame XML: %s' % IGameXMLFile
				txt = open(IGameXMLFile).read()
				try:
						txt = txt.decode('gbk').encode('utf-8')
				except:
						pass
						
				IGameXMLDoc = minixml.parseString(txt)
				print 'Done.'
				return IGameXMLDoc
		
		def __ExportSkeleton(self, IGameXMLDoc, aniName):
				""" Export skeleton """
				
				if not self._hasSkin:
						return
				
				# export all skeleton animation
				skeletonExport = CSkeletonExport()
				self._skeletonExportList.append(skeletonExport)
				skeletonFile = os.path.join(wx.GetApp()._mainFrame._dbbAniDir.GetValue(), '%s.ani.xml' % aniName)
				meshBone = wx.GetApp()._mainFrame._ckMeshBone.GetValue()
				meshBoneList = wx.GetApp()._mainFrame._editMeshBoneList.GetValue().encode('gbk').split(',')
				meshBoneList = map(str.strip, meshBoneList)         
				skeletonExport.DoExport(IGameXMLDoc, aniName, skeletonFile, meshBone, meshBoneList)        

		def __RemoveAllUnusedBones(self):
				""" Remove all unused bones """
				
				if not self._hasSkin:
						return
				
				# delete all bones that is not used by any vertices and bones.
				usedBone = set([])
				for meshExport in self._meshExportList:
						for vs in meshExport._vertexSkin.values():
								for inf in vs._bone:
										usedBone.add(inf[0])    # add used bone id
						
				for skeletonExport in self._skeletonExportList:

						boneDict = {}   
						for bone in skeletonExport._boneList:
								boneDict[bone._id] = bone		

						# add boneId which is usedBone's parent
						for boneId in copy.copy(usedBone):
								id = boneId
								while True:
										bone = boneDict[id]
										if not bone._parentId:
												break
												
										parentBone = boneDict[bone._parentId]
										parentBoneId = parentBone._id
										oldCount = len(usedBone)
										usedBone.add(parentBoneId)
										if len(usedBone) > oldCount:
												print 'Warning: bone[%s] is not used, but it is parent of a used bone' % parentBone._name
												
										id = parentBoneId
						
						i = 0
						for bone in skeletonExport._boneList[:]:
								# if the bone's id is in usedBone, then skip it
								if bone._id in usedBone:
										continue
										
								skeletonExport._boneList.remove(bone)
								i += 1

						print 'Remove %d bones from animation %s' % (i, skeletonExport._aniName)
						
		def __WriteModelFile(self, modelFile, modelName):
				""" Write the output model file """
				
				print 'Write model file: %s' % modelFile
				impl = minixml.getDOMImplementation()
				newDoc = impl.createDocument(None, None, None)
				modelElem = newDoc.createElement('Model')
				newDoc.childNodes.append(modelElem)
				
				modelElem.setAttributeNode(newDoc.createAttribute('ShaderFile'))
				modelElem.setAttribute('ShaderFile', 'shader/model.fx')     

				for skeletonExport in self._skeletonExportList:
						aniElem = newDoc.createElement('Animation')
						modelElem.childNodes.append(aniElem)
						
						aniElem.setAttributeNode(newDoc.createAttribute('AnimationFile'))
						aniFile = REMOVE_ROOT_PATH(skeletonExport._outputFile)
						aniElem.setAttribute('AnimationFile', aniFile)

				mtlIndexList = []
				for i in xrange(0, len(self._meshExportList)):
						meshExport = self._meshExportList[i]
						for subMesh in meshExport._subMeshList:            
								if not subMesh._d3dVertexList or not subMesh._d3dFaceIdxList:
										continue
																
								subMeshElem = newDoc.createElement('SubMesh')
								modelElem.childNodes.append(subMeshElem)
								
								subMeshElem.setAttributeNode(newDoc.createAttribute('MeshFile'))
								meshFile = REMOVE_ROOT_PATH(subMesh._outputFile)
								meshFile = meshFile.replace('\\', '/')
								subMeshElem.setAttribute('MeshFile', meshFile)
		
								try:
										try:
											mtl = self._mtlExport._materialList[subMesh._materialIndex]
											if mtl._subMaterialList:
												mtl = mtl._subMaterialList[subMesh._subMaterialIndex]
										except:
											print 'ERROR: material is not exist.'
										
										mtlIdx = mtlIndexList.index((subMesh._materialIndex, subMesh._subMaterialIndex))
								except:
										mtlIdx = len(mtlIndexList)
										mtlIndexList.append((subMesh._materialIndex, subMesh._subMaterialIndex))
								subMeshElem.setAttributeNode(newDoc.createAttribute('MaterialIdx'))
								subMeshElem.setAttribute('MaterialIdx', str(mtlIdx))
												
				for mtlIdx, subMtlIdx in mtlIndexList:
						mtl = self._mtlExport._materialList[mtlIdx]
						if mtl._subMaterialList:
								mtl = mtl._subMaterialList[subMtlIdx]
						mtlElem = newDoc.createElement('Material')
						modelElem.childNodes.append(mtlElem)
						
						fileName = os.path.join(wx.GetApp()._mainFrame._dbbMtlDir.GetValue(), modelName, mtl._fileName)
						fileName = REMOVE_ROOT_PATH(fileName)
						fileName = fileName.replace('\\', '/')
												
						mtlElem.setAttributeNode(newDoc.createAttribute('MaterialFile'))
						mtlElem.setAttribute('MaterialFile', fileName)

				newDoc.writexml(open(modelFile, 'w'), newl = '\r\n', addindent = '    ', encoding = 'utf-8')
				newDoc.unlink()        
												
		def ExportModel(self, modelFile):
				""" Export the model """
				
				self._mtlExport = None
				self._meshExportList = []
				self._skeletonExportList = []
				self._texFilePathList = []
				self._hasSkin = False
				
				modelName = self.__GetModelName(modelFile)
				
				# Setup the progress bar and calculate the step value.
				wx.GetApp()._mainFrame._progressBar.SetValue(0)
				pbInc = 100.0 / float(wx.GetApp()._mainFrame._listIGameXML.GetItemCount() + 5)
				
				# loop all IGame XML files.
				for i in xrange(0, wx.GetApp()._mainFrame._listIGameXML.GetItemCount()):
						IGameXMLFile = wx.GetApp()._mainFrame._listIGameXML.GetItemText(i)
						aniName = self.__GetAnimationName(IGameXMLFile)
						IGameXMLDoc = self.__ParseIGameXML(IGameXMLFile)
						
						# export material and mesh only from the first IGameXML
						if i == 0:
								self.__ExportMaterial(IGameXMLDoc, modelName)
								self.__ExportMesh(IGameXMLDoc, modelName)
								
						# export skeleton
						self.__ExportSkeleton(IGameXMLDoc, aniName)
						wx.GetApp()._mainFrame._progressBar.SetValue(wx.GetApp()._mainFrame._progressBar.GetValue() + int(pbInc))

				# remove all unused bones.
				self.__RemoveAllUnusedBones()
				wx.GetApp()._mainFrame._progressBar.SetValue(wx.GetApp()._mainFrame._progressBar.GetValue() + int(pbInc))
								
				# write mesh output file
				for meshExport in self._meshExportList:
						if self._hasSkin:
								meshExport.ProcessSkinBoneId(self._skeletonExportList[0])
						meshExport.WriteFile()
						
				wx.GetApp()._mainFrame._progressBar.SetValue(wx.GetApp()._mainFrame._progressBar.GetValue() + int(pbInc))
				
				# write material output file.
				self._mtlExport.WriteFile(self._meshExportList)        
				wx.GetApp()._mainFrame._progressBar.SetValue(wx.GetApp()._mainFrame._progressBar.GetValue() + int(pbInc))
						
				# write all animation file
				for skeletonExport in self._skeletonExportList:
						print 'Write animation file: %s' % skeletonExport._outputFile
						skeletonExport.WriteFile()
				
				wx.GetApp()._mainFrame._progressBar.SetValue(wx.GetApp()._mainFrame._progressBar.GetValue() + int(pbInc))
							 
				# write model file
				self.__WriteModelFile(modelFile, modelName)
				wx.GetApp()._mainFrame._progressBar.SetValue(100)
				
__builtins__['CONVERTER'] = CIGameConverter()

